/*
 * 代号：凤凰
 * 2014年6月30日
 * V3.0
 */
package com.jphenix.servlet.filter;

//#region 【引用区】
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import com.jphenix.driver.cluster.ClusterFilter;
import com.jphenix.driver.cluster.ServerInfoVO;
import com.jphenix.servlet.parent.ServiceBeanParent;
import com.jphenix.share.lang.SBoolean;
import com.jphenix.share.lang.SInteger;
import com.jphenix.share.lang.SString;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.BytesArrayUtil;
import com.jphenix.standard.beans.ITriggerInit;
import com.jphenix.standard.docs.BeanInfo;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.servlet.IFilter;
import com.jphenix.standard.servlet.IRequestManager;
import com.jphenix.standard.servlet.IResponseManager;
import com.jphenix.standard.servlet.api.IFilterConfig;
import com.jphenix.standard.servlet.api.IRequest;
import com.jphenix.standard.servlet.api.IResponse;
import com.jphenix.standard.viewhandler.IViewHandler;
//#endregion

//#region 【说明区】
/**
 * 代理过滤器
 * 
 * 
 * 在配置文件中配置
 * 
 * com.jphenix.servlet.filter.ProxyFilter
 * 
 * 代理配置信息：
 * <not_used_cluster_conf>注释2</not_used_cluster_conf>
 * <proxy>
 *   <server disabled="注释3" id="注释4" url="注释5" />    
 * </proxy>
 * 
 * 
 * 注释2：是否不使用集群配置信息  1不使用  0使用 （或不设置该段） 通常不配置这个，
 *        都使用集群配置信息。除非要代理的系统是另外一套没跟本机做集群的不同的系统。
 * 注释3：0不禁用或者不设置该属性     1禁用   
 * 注释4：被代理的服务器主键，这个主键值在整个代理配置信息中不能跟别的服务器主键值重复。
 * 注释5：被代理服务器访问根url 比如：http://192.168.0.1:8080/manage
 *        注意：url中要带端口号，manage虚拟路径
 *        
 *        
 * 注意：该过滤器默认会读取集群配置信息，如果存在集群信息的话。这样就不用单独再配置代理信息。
 * 注意：集群中的应用应该是一模一样的。
 * 注意：如果是通过代理访问进来的请求，报文头中会有这几个主键：
 *                   _from_proxy="1"   
 *                   _proxy_server="来源服务器名"  
 *                   _proxy_group="来源服务器所在分组"
 * 
 * 2019-01-24 修改了父类的类路径
 * 2019-04-11 修改了字节数组中替换指定字节段方法入参
 * 2019-06-15 按照IFilter增加了过滤器初始化方法
 * 2019-07-16 修改了从配置文件中获取信息的方法
 * 2019-08-19 优化了流，增加了缓存
 * 2019-08-27 增加了来源服务器信息标识，是否代理访问标识
 * 2019-09-18 增加了发送服务器标识
 * 2020-03-09 判断了返回内容如果是json格式，则不对其路径做转换
 * 2020-06-18 增加了在url中如果包含关键字_no_rewrite_=1,代理返回的内容不做虚拟路径替换（比如用在返回内容中包含了虚拟路径关键字，但并不是虚拟路径）
 * 2020-08-03 修改了响应代理逻辑
 * 2022-09-04 隔离了ServletApi，兼容新老Tomcat
 * 
 * @author 马宝刚
 */
//#endregion
@ClassInfo({"2024-07-14 13:42","代理过滤器"})
@BeanInfo({"proxyfilter"})
public class ProxyFilter extends ServiceBeanParent implements IFilter,ITriggerInit {

	//#region 【声明区】
    //代理url对照容器   key：代理主键，value被代理url（包含上下文）
    protected Map<String,String> urlMap                     = new HashMap<String,String>();
    //上下文对照容器 key：代理主键，value目标上下文
    protected Map<String,String> contextPathMap             = new HashMap<String,String>();
    protected int                outTime                    = 300000; // 超时时间（5分钟）
    protected String             localContextPath           = null;   // 本地上下文
    private   String             localServerName            = null;   // 当前服务器主键
    private   String             localGroupName             = null;   // 当前服务器群组名
    //#endregion
    
    //#region getIndex() 获取排序索引，数值越小越先执行
    /**
     * 获取排序索引，数值越小越先执行
     * 刘虻
     * 2010-5-25 下午03:15:01
     * @return 排序索引
     */
    @Override
    public int getIndex() {
        return 4;
    }
    //#endregion

    //#region getFilterActionExtName() 获取需要过滤的动作路径扩展名
    /**
     * 获取需要过滤的动作路径扩展名
     * @return 扩展名
     * 2014年9月12日
     * @author 马宝刚
     */
    @Override
    public String getFilterActionExtName() {
        return "*";
    }
    //#endregion
    
    //#region getProxyUrl(proxyKey) 返回代理目标URL
    /**
     * 返回代理目标URL
     * @param proxyKey 代理信息主键
     * @return 代理目标URL
     * 2016年7月18日
     * @author MBG
     */
    public String getProxyUrl(String proxyKey) {
    	return str(urlMap.get(proxyKey))+str(contextPathMap.get(proxyKey));
    }
    //#endregion

    //#region doFilter(req,resp) 执行过滤
    /**
     * 执行过滤
     * 刘虻
     * 2010-5-25 下午04:09:25
     * @param req 页面请求
     * @param resp 页面反馈
     * @return 如果返回真，则不继续往下进行，直接跳过结束
     * @throws Exception 执行发生异常
     */
	@SuppressWarnings("unchecked")
	@Override
    public boolean doFilter(IRequestManager req, IResponseManager resp) throws Exception {
        //代理目标主键
        String proxyKey = str(req.iGetSession().getAttribute("proxy_key"));
        if(req.getServletPath().endsWith("/__stop_proxy.ha")) {
			//终止代理
        	req.iGetSession().removeAttribute("proxy_key");
        	req.iGetSession().removeAttribute("proxy_session_map");
        	
			//终止后的跳转路径
			String reDir = str(req.getParameter("redir"));
			log("The Proxy Key:["+proxyKey+"] Has Stoped Redirect:["+reDir+"]");
			if(reDir.length()>0) {
				resp.sendRedirect(reDir);
			}else {
				resp.getWriter().print("The Proxy Has Stoped");
			}
			return true;
		}

        if(proxyKey.length()<1) {
        	return false;
        }
        //目标上下文
        String contextPath = str(contextPathMap.get(proxyKey)) ;
        
        //目标url
        String toUrl = urlMap.get(proxyKey)+contextPath;
        if(toUrl==null || toUrl.length()<1) {
            return false;
        }
        //动作路径（不带虚拟路径）
        String sPath = req.getServletPath();
        if(sPath!=null) {
        	toUrl+=sPath;
        }
        //目标服务器的会话信息
        Map<String,String> sessionMap = (Map<String,String>)req.iGetSession().getAttribute("proxy_session_map");
        if(sessionMap==null) {
        	sessionMap = new HashMap<String,String>();
        	req.iGetSession().setAttribute("proxy_session_map",sessionMap);
        }
        //提交参数
        String queryStr = req.getQueryString();
        
        //是否不需要重写内容
        boolean noRewrite = queryStr.indexOf("_no_rewrite_=1")>-1;
        
        //这个日志太频繁了，刷屏，故屏蔽掉
        //if(debugMode()) {
        //	debug("=== In Proxy Key:["+proxyKey+"] Url:["+sPath+"] QueryString:["+queryStr+"]");
        //}
        if(queryStr!=null && queryStr.length()>0) {
        	toUrl += "?"+queryStr;
        }
        //自定义头
        Map<String,String> headerMap = req.getCustomHeaderMap();

        resp.setHeader("_cluster_call_",localServerName);
        resp.setHeader("_from_proxy","1");
        resp.setHeader("_proxy_server",localServerName);
        resp.setHeader("_proxy_group",localGroupName);
        
        //System.out.println("\n\n========ProxyFilter=====本机会话：["
        //+req.getSession().getId()+"]  目标会话：["+sessionMap.get("Set-Cookie")+"]  proxyKey:["+proxyKey+"]\n");
        if(contextPath==null || contextPath.length()<1 || localContextPath.equals(contextPath)) {
        	call(toUrl,sessionMap,headerMap,outTime,req,resp,resp.iGetOutputStream(),null);
        }else {
        	//设置了上下文，就得将调用后返回的信息内容中，将目标上下文，转换为本地上下文，否则再提交时，如果带上了目标上下文，就
        	//不会请求到这里了。
        	
        	//输出流缓存
        	ByteArrayOutputStream bos = new ByteArrayOutputStream();
        	
        	//获取输出流
        	OutputStream respOs = resp.iGetOutputStream();
        	
        	call(toUrl,sessionMap,headerMap,outTime,req,resp,respOs,bos);
            
        	//返回信息格式
        	String contentType = str(resp.getHeader("Content-Type"));
        	
            //输出替换后的数据
            if(bos.size()>0) {
            	//如果返回的是Json数据，或者是XML格式数据，则不对其内容做路径转换，只针对HTML内容做虚拟路径转换
            	if(noRewrite || contentType.indexOf("application/json")>-1 || contentType.indexOf("text/xml")>-1) {
            		respOs.write(bos.toByteArray());
            	}else {
                    respOs.write(
                    		BytesArrayUtil.arrayReplace(
                    				 bos.toByteArray()
                    				,contextPath.getBytes()
                    				,localContextPath.getBytes()));
            	}
            }
        }
        return true;
    }
    //#endregion
    
	//#region init(clusterObj) 执行初始化
    /**
     * 执行初始化
     * @throws Exception 异常
     * 2016年1月11日
     * @author 马宝刚
     */
    @Override
    public void init(Object clusterObj) throws Exception {
        //过滤器管家
        BaseFilter fe = getBean(BaseFilter.class);
        //上下文  要么为空，要么为  /路径   字符串的末尾不能是/
        localContextPath = fe.getContextPath();
    	if(localContextPath==null || "/".equals(localContextPath)) {
			localContextPath = "";
		}
        urlMap         = new HashMap<String,String>();
        contextPathMap = new HashMap<String,String>();
        String key;         //代理主键
        String url;         //代理URL
        String contextPath; //上下文
        int    point;       //字符串分隔符位置
        //获取代理信息序列
        List<IViewHandler> cList = px("proxy/server").c();
        for(IViewHandler ele:cList) {
            if(SBoolean.valueOf(ele.a("disabled"))) {
                continue;
            }
            url         = ele.a("url").trim();
            key         = ele.a("id").trim();
            if(url.endsWith("/")) {
            	url = url.substring(0,url.length()-1);
            }
            point = url.indexOf("//");
            if(point<0) {
            	//无效url
            	continue;
            }
            point = url.indexOf("/",point+2);
            if(point>0) {
            	contextPath = url.substring(point);
            }else {
            	contextPath = "";
            }
            if(contextPath.endsWith("/")) {
            	contextPath = contextPath.substring(0,contextPath.length()-1);
            }
            if(key.length()>0 && url.length()>0) {
                urlMap.put(key,url);
                contextPathMap.put(key,contextPath);
            }
        }
        
        //集群处理类
        if(clusterObj instanceof ClusterFilter) {
        	//集群管理类
        	ClusterFilter cluster = (ClusterFilter)clusterObj;
        	
        	localServerName = cluster.name();
        	localGroupName  = cluster.group();
        	
        	if(cluster.clusterMode()) {
        		//适用集群配置信息序列
            	List<ServerInfoVO> infoList = cluster.getServerInfoList();
            	//动作上下文
            	
                for(ServerInfoVO siVO:infoList) {
                    if(siVO.disabled) {
                        continue;
                    }
                    key = siVO.name;
                    url = siVO.url;
                    if(url==null) {
                    	continue;
                    }
                    if(url.endsWith("/")) {
                    	url = url.substring(0,url.length()-1);
                    }
                    point = url.indexOf("//");
                    if(point<0) {
                    	//无效url
                    	continue;
                    }
                    point = url.indexOf("/",point+2);
                    if(point>0) {
                    	contextPath = url.substring(point);
                    }else {
                    	if(siVO.context!=null && siVO.context.length()>0) {
                    		contextPath = siVO.context;
                    	}else {
                    		contextPath = "";
                    	}
                    }
                    if(contextPath.endsWith("/")) {
                    	contextPath = contextPath.substring(0,contextPath.length()-1);
                    }
                    urlMap.put(key,url);
                    contextPathMap.put(key,contextPath);
                }
        	}
        }
        
        //注册到过滤器管理类中
        if(beanExist(FilterVector.class)) {
        	bean(FilterVector.class).regist(this);
        }
        
        //输出日志
        StringBuffer logSbf = new StringBuffer();
        logSbf.append("\n******************************************\nThe ProxyFilter Has Enabled\n  Server:\n");
        //服务器主键序列
        List<String> keyList = BaseUtil.getMapKeyList(urlMap);
        for(String ele:keyList) {
        	logSbf.append("    "+ele+":[").append(urlMap.get(ele)+"]\n");
        }
        logSbf.append("******************************************\n\n");
        log.startLog(logSbf);
    }
    //#endregion
    
    //#region init(bf,config) 执行初始化
	/**
	 * 执行初始化
	 * @param bf         过滤器管理类
	 * @param config     Servlet配置信息类
	 * @throws Exception 异常（如果初始化发生异常，则放弃不再使用）
	 * 2019年6月15日
	 * @author MBG
	 */
	@Override
	public void init(BaseFilter bf, IFilterConfig config) throws Exception {}
	//#endregion
    
    /**
     * 调用URL并直接输出
     * @param sUrl      调用url
     * @param postIs    提交的数据流
     * @param headerMap 报文头
     * @param os         输出流
     * @param outTime 超时时间
     * @throws Exception 异常
     * 2015年11月16日
     * @author 马宝刚
     */
	private void call(
            String sUrl
            ,Map<String,String> headerMap
            ,Map<String,String> customHeaderMap
            ,int outTime
            ,IRequest req
            ,IResponse resp
            ,OutputStream respOs
            ,OutputStream dataOs) throws Exception {
        if(headerMap==null) {
            headerMap = new HashMap<String,String>();
        }
        int         rCount;                      //读取字节数
        byte[]      buffer     = new byte[2048]; //读入缓存
        InputStream is         = null;           //读入信息流
        int         statusCode = 0;              //返回状态码
        String      statusMsg  = "";             //返回状态信息
        InputStream postIs     = null;           //提交数据读入流
        
        //如果是文件下载，直接用resp的输出流，否则用字节缓存流
        //然后做批量替换虚拟路径
        OutputStream callOs = null; //调用后返回流
        try {
            //构造URL
            URL url = new URL(sUrl);
            //构造链接
            URLConnection urlc = url.openConnection();
            if(outTime>0) {
                urlc.setConnectTimeout(outTime);
                urlc.setReadTimeout(outTime);
            }
            if(urlc instanceof HttpsURLConnection) {
                //操老外的妈，通常调用的路径都是内部指定的，非得验证域名，导致域名验证失败
                ((HttpsURLConnection)urlc).setHostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });
                ((HttpsURLConnection)urlc).setSSLSocketFactory(createSSLContext().getSocketFactory());
            }
            //设置不自动跳转获取数据（因为自动跳转时不会自动将cookie信息带过去，会导致session丢失）
            ((HttpURLConnection)urlc).setInstanceFollowRedirects(false);
            
            if(customHeaderMap!=null) {
                //设置自定义头信息
                String[] keyList = BaseUtil.getMapKeys(customHeaderMap);
                String keyValue;
                for(int i=0;i<keyList.length;i++) {
                	keyValue = SString.valueOf(customHeaderMap.get(keyList[i]));
                	if(keyValue.length()<1) {
                		continue;
                	}
                	urlc.setRequestProperty(keyList[i],keyValue);
                }
            }
            setHeaderInfo(urlc,headerMap,req.getContentType(),0,null); //设置头信息
            urlc.setDoOutput(true);   
            urlc.setUseCaches(false);
            
            if(!"GET".equalsIgnoreCase(req.getMethod())) {
            	//获取提交信息读入流
            	postIs = req.iGetInputStream();
            	if(postIs!=null) {
                    //构建输出信息流
                    OutputStream out = urlc.getOutputStream();
                    while((rCount=postIs.read(buffer))!=-1) {
                    	out.write(buffer,0,rCount);
                    }
            	}
            }
            statusCode = ((HttpURLConnection)urlc).getResponseCode();
            statusMsg = ((HttpURLConnection)urlc).getResponseMessage();
            resp.sendError(statusCode,statusMsg);
            fixHeadInfo(urlc,headerMap); //处理 头信息
            //如果服务器端返回重定向信息，则自动跳转到重定向页面并且将cookie信息也带过去（内置方法不会带cookie过去，导致会话丢失）
            if(((HttpURLConnection)urlc).getResponseCode()==HttpURLConnection.HTTP_MOVED_TEMP  ||
            		((HttpURLConnection)urlc).getResponseCode()==HttpURLConnection.HTTP_MOVED_PERM) {
            	//重定向URL
            	String goUrl = SString.valueOf(headerMap.get("Location"));
            	if(goUrl.indexOf("://")<0) {
            		goUrl = getBaseUrl(sUrl)+goUrl;
            	}
            	if(goUrl.indexOf("://")<0) {
            		goUrl = getBaseUrl(sUrl)+goUrl;
            	}
            	if(log!=null) {
            		log.log("HttpCall SendRedirectURL:["+goUrl+"]");
            	}
            	//调用重定向路径
            	call(goUrl,null,customHeaderMap,outTime,req,resp,respOs,dataOs); 
            	return;
            }
            //头主键
            String headerKey = "Content-Disposition";
            //头值
            String headerValue = SString.valueOf(headerMap.remove(headerKey));
            if(headerValue.length()>0) {
            	resp.setHeader(headerKey,headerValue);
            	
            	//如果是文件下载，就设置内容长度
            	headerKey = "Content-Length";
            	headerValue = SString.valueOf(headerMap.remove(headerKey));
            	if(headerValue.length()>0) {
            		resp.setContentLength(SInteger.valueOf(headerValue));
            	}
            }
            callOs = dataOs==null?respOs:dataOs;
            
            headerKey = "Content-Type";
            headerValue = SString.valueOf(headerMap.get(headerKey));
            if(headerValue.length()>0) {
            	resp.setContentType(headerValue);
            }
            headerKey = "Content-Encoding";
            headerValue = SString.valueOf(headerMap.get(headerKey));
            if(headerValue.length()>0) {
            	resp.setHeader(headerKey,headerValue);
            }
            headerKey = "Content-Language";
            headerValue = SString.valueOf(headerMap.get(headerKey));
            if(headerValue.length()>0) {
            	resp.setHeader(headerKey,headerValue);
            }
            //获取信息流
            is = urlc.getInputStream();
            while ((rCount=is.read(buffer))!=-1) {
            	callOs.write(buffer,0,rCount);
            }
        } catch (IOException e) {
            //e.printStackTrace();
            //构建错误信息
            String msg = "HttpCall Exception URL:["+sUrl+"] Params:[InputStream] e:["
                    +e+"] HTTP Status:["+statusCode+"] Msg:["+statusMsg+"]";
            System.err.print(msg);
            return;
            //已经往resp中设置了状态码和错误信息
            //throw new MsgException(HttpCall.class,msg);
        }finally {
        	if(postIs!=null) {
                try {
                	postIs.close();
                } catch (Exception e2) {}
        	}
            try {
                is.close();
            } catch (Exception e2) {}
            try {
            	callOs.flush();
            }catch(Exception e2) {}
        }
    }
    
    /**
     * 获取根URL
     * @param url 目标URL
     * @return 根URL
     * 2014年8月23日
     * @author 马宝刚
     */
    protected String getBaseUrl(String url) {
    	//构建返回值
    	StringBuffer reSbf = new StringBuffer();
    	
    	int point = url.indexOf("//"); //分割点
    	if(point<0) {
    		return url;
    	}
    	reSbf.append(url, 0, point+2);
    	url = url.substring(point+2);
    	
    	point = url.indexOf("/");
    	if(point<0) {
    		reSbf.append(url);
    	}else {
    		reSbf.append(url, 0, point);
    	}
    	return reSbf.toString();
    }
    
    /**
     * 获取代理目标服务器信息容器
     * @return 代理目标服务器信息容器
     * 2017年3月26日
     * @author MBG
     */
    public Map<String,String> getUrlMap(){
    	//构建返回值，并防止外部程序注入新的服务器信息
    	HashMap<String,String> reMap = new HashMap<String,String>();
    	reMap.putAll(urlMap);
    	return reMap;
    }
    
    /**
     * 获取代理目标服务器名序列
     * @return 代理目标服务器名序列
     * 2017年3月27日
     * @author MBG
     */
    public List<String> getServerNameList(){
    	return BaseUtil.getMapKeyList(urlMap);
    }
    
    
    /**
     * 将头信息放入容器
     * @author 马宝刚
     * @param urlc 连接对象
     * @param headerMap 头信息容器
     * 2009-4-1  下午08:21:39
     */
	@SuppressWarnings({ "rawtypes", "unchecked" })
    public void fixHeadInfo(URLConnection urlc,Map headerMap) {
    	if (headerMap==null) {
    		return;
    	}
    	String value; //参数值
    	int point; //字符串分割点
    	//没有结束上限，在循环体中退出，大于索引数时不会抛错
    	for(int i=0;;i++) {
    		//头主键
    		String headerKey = SString.valueOf(urlc.getHeaderFieldKey(i));
    		//头值
    		String headerValue = SString.valueOf(urlc.getHeaderField(i));
    		if (headerKey.length()<1 && headerValue.length()<1) {
    			//如果主键和值都为空，头结束
    			return;
    		}
    		if (headerKey.length()<1|| headerValue.length()<1) {
    		    //主键和值其中一个为空，忽略
    			continue;
    		}
    		//头部值序列  
    		/*
    		 * 服务器返回Cookie时，可能会返回多个，比如：
    		 * Set-Cookie: JSESSIONID=69FA0483FD80E8E4B4A85352B6FAB8BB; Path=/app/; HttpOnly
    		 * Set-Cookie: PXSESSIONID=PX-14295943421520-938; Path=/app
    		 * Set-Cookie: PXSERVERID=app_server3; Path=/app
    		 * 
    		 * 但是我们发送的Cookie格式并不是返回的这种格式，而是这种格式
    		 * Cookie: PXSESSIONID=PX-14295943421520-938; PXSERVERID=app_server1; JSESSIONID=PX1429753309470-14297533094700-29
    		 */
    		if("set-cookie".equalsIgnoreCase(headerKey)) {
    		    value = SString.valueOf(headerMap.get(headerKey));
    		    if(value.length()>0) {
    		        value += "; ";
    		    }
    		    point = headerValue.indexOf(";");
    		    if(point>-1) {
    		        headerValue = headerValue.substring(0,point);
    		    }
    		    value+=headerValue;
    		    headerMap.put(headerKey,value);
    		}else {
    		    headerMap.put(headerKey,headerValue);
    		}
    	}
    }
	
    
    
    /**
     * 设置头信息
     * @param conn            URL连接对象
     * @param headerMap       头信息容器
     * @param contentTypeStr  已经定义好的内容类型
     * @param contentType     发送内容类型  0html  1xml  2json
     * @param sendEncoding    发送内容编码
     * @throws Exception      异常
     * 2015年4月22日
     * @author 马宝刚
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private void setHeaderInfo(
            URLConnection urlc,Map headerMap
            ,String contentTypeStr
            ,int contentType
            ,String sendEncoding) throws Exception {
        //设置头信息
        String[] keyList = BaseUtil.getMapKeys(headerMap);
        String value; //头信息值
        for(String key:keyList) {
            if("user-agent".equalsIgnoreCase(key) || "Content-Type".equalsIgnoreCase(key)) {
                continue;
            }
            value = SString.valueOf(headerMap.get(key));
            if(value.length()<1) {
                continue;
            }
            //判断cookie是将大小写统一
            if("set-cookie".equalsIgnoreCase(key) || "cookie".equalsIgnoreCase(key)) {
                key = "Cookie";
            }
            urlc.setRequestProperty(key,value);
        }
        urlc.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36");
        if(contentTypeStr!=null && contentTypeStr.length()>0) {
            urlc.setRequestProperty("Content-Type",contentTypeStr);
        }else {
            if (contentType==1) {
                //xml
                if(sendEncoding==null || sendEncoding.length()<1) {
                    urlc.setRequestProperty("Content-Type","text/xml; charset=UTF-8");
                }else {
                    urlc.setRequestProperty("Content-Type","text/xml; charset="+sendEncoding);
                }
            }else if(contentType==2) {
                //json
                if(sendEncoding==null || sendEncoding.length()<1) {
                    urlc.setRequestProperty("Content-Type","application/json; charset=UTF-8");
                }else {
                    urlc.setRequestProperty("Content-Type","application/json; charset="+sendEncoding);
                }
            }else {
                //text
                urlc.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
            }
        }
    }
    
    
	/**
	 * 构建无需私钥的上下文对象
	 * @return 上下文对象
	 * 2015年1月20日
	 * @author 马宝刚
	 */
    private SSLContext createSSLContext() {
        SSLContext sslcontext = null; //构建返回值
        try {
             sslcontext = SSLContext.getInstance("TLS");
             sslcontext.init(null, new TrustManager[] { new X509TrustManager() {
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
                @Override
                public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
                @Override
                public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
            }}, null);
        }catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
       } catch (KeyManagementException e) {
           e.printStackTrace();
      }
        SSLContext.setDefault(sslcontext);
        return sslcontext;
    }
    
    /**
     * 覆盖方法
     */
    @Override
    public void destroy() {}
}
